2 * Copyright (c) 2017-2019 Apple Inc. All rights reserved.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 #include "unittest_common.h"
18 #import <XCTest/XCTest.h>
20 struct UDPSocket_struct
22 mDNSIPPort port; // MUST BE FIRST FIELD -- mDNSCoreReceive expects every UDPSocket_struct to begin with mDNSIPPort port
24 typedef struct UDPSocket_struct UDPSocket;
26 // This client request was generated using the following command: "dns-sd -Q 123server.dotbennu.com. A".
27 uint8_t query_client_msgbuf[35] = {
28 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0x32, 0x33, 0x73, 0x65, 0x72, 0x76, 0x65,
29 0x72, 0x2e, 0x64, 0x6f, 0x74, 0x62, 0x65, 0x6e, 0x6e, 0x75, 0x2e, 0x63, 0x6f, 0x6d, 0x00, 0x00,
33 // This uDNS message is a canned response that was originally captured by wireshark.
34 uint8_t query_response_msgbuf[108] = {
35 0x69, 0x41, // transaction id
37 0x00, 0x01, // 1 question for 123server.dotbennu.com. Addr
38 0x00, 0x02, // 2 anwsers: 123server.dotbennu.com. CNAME test212.dotbennu.com., test212.dotbennu.com. Addr 10.100.0.1,
39 0x00, 0x01, // 1 authorities anwser: dotbennu.com. NS cardinal2.apple.com.
40 0x00, 0x00, 0x09, 0x31, 0x32, 0x33,
41 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x08, 0x64, 0x6f, 0x74, 0x62, 0x65, 0x6e, 0x6e, 0x75, 0x03,
42 0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01, 0xc0, 0x0c, 0x00, 0x05, 0x00, 0x01, 0x00, 0x00,
43 0x02, 0x56, 0x00, 0x0a, 0x07, 0x74, 0x65, 0x73, 0x74, 0x32, 0x31, 0x32, 0xc0, 0x16, 0xc0, 0x34,
44 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x04, 0x0a, 0x64, 0x00, 0x01, 0xc0, 0x16,
45 0x00, 0x02, 0x00, 0x01, 0x00, 0x01, 0x51, 0x80, 0x00, 0x12, 0x09, 0x63, 0x61, 0x72, 0x64, 0x69,
46 0x6e, 0x61, 0x6c, 0x32, 0x05, 0x61, 0x70, 0x70, 0x6c, 0x65, 0xc0, 0x1f
49 // Variables associated with contents of the above uDNS message
50 #define uDNS_TargetQID 16745
51 char udns_original_domainname_cstr[] = "123server.dotbennu.com.";
52 char udns_cname_domainname_cstr[] = "test212.dotbennu.com.";
53 static const mDNSv4Addr dns_response_ipv4 = {{ 10, 100, 0, 1 }};
55 @interface CNameRecordTest : XCTestCase
57 UDPSocket* local_socket;
58 request_state* client_request_message;}
61 @implementation CNameRecordTest
63 // The InitThisUnitTest() initializes the mDNSResponder environment as well as
64 // a DNSServer. It also allocates memory for a local_socket and client request.
65 // Note: This unit test does not send packets on the wire and it does not open sockets.
68 // Init unit test environment and verify no error occurred.
69 mStatus result = init_mdns_environment(mDNStrue);
70 XCTAssertEqual(result, mStatus_NoError);
72 // Add one DNS server and verify it was added.
74 XCTAssertEqual(CountOfUnicastDNSServers(&mDNSStorage), 1);
76 // Create memory for a socket that is never used or opened.
77 local_socket = (UDPSocket *) mDNSPlatformMemAllocateClear(sizeof(*local_socket));
79 // Create memory for a request that is used to make this unit test's client request.
80 client_request_message = calloc(1, sizeof(request_state));
85 mDNS *m = &mDNSStorage;
86 request_state* req = client_request_message;
87 DNSServer *ptr, **p = &m->DNSServers;
91 reply_state *reply = req->replies;
92 req->replies = req->replies->next;
93 mDNSPlatformMemFree(reply);
95 mDNSPlatformMemFree(req);
97 mDNSPlatformMemFree(local_socket);
103 LogInfo("FinalizeUnitTest: Deleting server %p %#a:%d (%##s)", ptr, &ptr->addr, mDNSVal16(ptr->port), ptr->domain.c);
104 mDNSPlatformMemFree(ptr);
108 - (void)testCNameRecordTestSeries
110 [self _startClientQueryRequest];
111 [self _populateCacheWithClientResponseRecords];
112 [self _simulateNetworkChangeAndVerify];
115 // This test simulates a uds client request by setting up a client request and then
116 // calling mDNSResponder's handle_client_request. The handle_client_request function
117 // processes the request and starts a query. This unit test verifies
118 // the client request and query were setup as expected. This unit test also calls
119 // mDNS_execute which determines the cache does not contain the new question's
121 - (void)_startClientQueryRequest
123 mDNS *const m = &mDNSStorage;
124 request_state* req = client_request_message;
125 char *msgptr = (char *)query_client_msgbuf;
126 size_t msgsz = sizeof(query_client_msgbuf);
127 mDNSs32 min_size = sizeof(DNSServiceFlags) + sizeof(mDNSu32) + 4;
129 mStatus err = mStatus_NoError;
130 char qname_cstr[MAX_ESCAPED_DOMAIN_NAME];
132 // Process the unit test's client request
133 start_client_request(req, msgptr, msgsz, query_request, local_socket);
134 XCTAssertEqual(err, mStatus_NoError);
136 // Verify the request fields were set as expected
137 XCTAssertNil((__bridge id)req->next);
138 XCTAssertNil((__bridge id)req->primary);
139 XCTAssertEqual(req->sd, client_req_sd);
140 XCTAssertEqual(req->process_id, client_req_process_id);
141 XCTAssertFalse(strcmp(req->pid_name, client_req_pid_name));
142 XCTAssertEqual(req->validUUID, mDNSfalse);
143 XCTAssertEqual(req->errsd, 0);
144 XCTAssertEqual(req->uid, client_req_uid);
145 XCTAssertEqual(req->ts, t_complete);
146 XCTAssertGreaterThan((mDNSs32)req->data_bytes, min_size);
147 XCTAssertEqual(req->msgend, msgptr+msgsz);
148 XCTAssertNil((__bridge id)(void*)req->msgbuf);
149 XCTAssertEqual(req->hdr.version, VERSION);
150 XCTAssertNil((__bridge id)req->replies);
151 XCTAssertNotEqual(req->terminate, (req_termination_fn)0);
152 XCTAssertEqual(req->flags, kDNSServiceFlagsReturnIntermediates);
153 XCTAssertEqual(req->interfaceIndex, kDNSServiceInterfaceIndexAny);
155 // Verify the query fields were set as expected
156 q = &req->u.queryrecord.op.q;
157 XCTAssertNotEqual(q, (DNSQuestion *)mDNSNULL);
158 XCTAssertEqual(q, m->Questions);
159 XCTAssertEqual(q, m->NewQuestions);
160 XCTAssertEqual(q->SuppressUnusable, mDNSfalse);
161 XCTAssertEqual(q->ReturnIntermed, mDNStrue);
162 XCTAssertEqual(q->Suppressed, mDNSfalse);
164 ConvertDomainNameToCString(&q->qname, qname_cstr);
165 XCTAssertFalse(strcmp(qname_cstr, udns_original_domainname_cstr));
166 XCTAssertEqual(q->qnamehash, DomainNameHashValue(&q->qname));
168 XCTAssertEqual(q->InterfaceID, mDNSInterface_Any);
169 XCTAssertEqual(q->flags, req->flags);
170 XCTAssertEqual(q->qtype, 1);
171 XCTAssertEqual(q->qclass, 1);
172 XCTAssertEqual(q->LongLived, 0);
173 XCTAssertEqual(q->ExpectUnique, mDNSfalse);
174 XCTAssertEqual(q->ForceMCast, 0);
175 XCTAssertEqual(q->TimeoutQuestion, 0);
176 XCTAssertEqual(q->WakeOnResolve, 0);
177 XCTAssertEqual(q->UseBackgroundTraffic, 0);
178 XCTAssertEqual(q->ValidationRequired, 0);
179 XCTAssertEqual(q->ValidatingResponse, 0);
180 XCTAssertEqual(q->ProxyQuestion, 0);
181 XCTAssertNotEqual((void*)q->QuestionCallback, (void*)mDNSNULL);
182 XCTAssertNil((__bridge id)q->DNSSECAuthInfo);
183 XCTAssertNil((__bridge id)(void*)q->DAIFreeCallback);
184 XCTAssertEqual(q->AppendSearchDomains, 0);
185 XCTAssertNil((__bridge id)q->DuplicateOf);
187 // Call mDNS_Execute to see if the new question, q, has an answer in the cache.
188 // It won't be yet because the cache is empty.
189 m->NextScheduledEvent = mDNS_TimeNow_NoLock(m);
192 // Verify mDNS_Execute processed the new question.
193 XCTAssertNil((__bridge id)m->NewQuestions);
195 // Verify the cache is empty and the request got no reply.
196 XCTAssertEqual(m->rrcache_totalused, 0);
197 XCTAssertNil((__bridge id)req->replies);
200 // This unit test receives a canned uDNS response message by calling the mDNSCoreReceive() function.
201 // It then verifies cache entries were added for the CNAME and A records that were contained in the
202 // answers of the canned response, query_response_msgbuf. This unit test also verifies that
203 // 2 add events were generated for the client.
204 - (void)_populateCacheWithClientResponseRecords
206 mDNS *const m = &mDNSStorage;
207 DNSMessage *msgptr = (DNSMessage *)query_response_msgbuf;
208 size_t msgsz = sizeof(query_response_msgbuf);
209 struct reply_state *reply;
210 request_state* req = client_request_message;
211 DNSQuestion *q = &req->u.queryrecord.op.q;
214 char name[kDNSServiceMaxDomainName];
215 uint16_t rrtype, rrclass, rdlen;
218 char domainname_cstr[MAX_ESCAPED_DOMAIN_NAME];
220 // Receive and populate the cache with canned response
221 receive_response(req, msgptr, msgsz);
223 // Verify 2 cache entries for CName and A record are present
224 mDNSu32 CacheUsed =0, notUsed =0;
225 LogCacheRecords_ut(mDNS_TimeNow(m), &CacheUsed, ¬Used);
226 XCTAssertEqual(CacheUsed, m->rrcache_totalused);
227 XCTAssertEqual(CacheUsed, 4); // 2 for the CacheGroup object plus 2 for the A and CNAME records
228 XCTAssertEqual(m->PktNum, 1); // one packet was received
230 // Verify question's qname is now set with the A record's domainname
231 ConvertDomainNameToCString(&q->qname, domainname_cstr);
232 XCTAssertEqual(q->qnamehash, DomainNameHashValue(&q->qname));
233 XCTAssertFalse(strcmp(domainname_cstr, udns_cname_domainname_cstr));
235 // Verify client's add event for CNAME is properly formed
236 reply = req->replies;
237 XCTAssertNotEqual(reply, (reply_state*)mDNSNULL);
238 XCTAssertNil((__bridge id)reply->next);
240 data = (char *)&reply->rhdr[1];
241 end = data+reply->totallen;
242 get_string(&data, data+reply->totallen, name, kDNSServiceMaxDomainName);
243 rrtype = get_uint16(&data, end);
244 rrclass = get_uint16(&data, end);
245 rdlen = get_uint16(&data, end);
246 rdata = get_rdata(&data, end, rdlen);
247 len = get_reply_len(name, rdlen);
249 XCTAssertEqual(reply->totallen, len + sizeof(ipc_msg_hdr));
250 XCTAssertEqual(reply->mhdr->version, VERSION);
251 XCTAssertEqual(reply->mhdr->datalen, len);
252 XCTAssertEqual(reply->mhdr->ipc_flags, 0);
253 XCTAssertEqual(reply->mhdr->op, query_reply_op);
255 XCTAssertEqual(reply->rhdr->flags, htonl(kDNSServiceFlagsAdd));
256 XCTAssertEqual(reply->rhdr->ifi, kDNSServiceInterfaceIndexAny);
257 XCTAssertEqual(reply->rhdr->error, kDNSServiceErr_NoError);
259 XCTAssertEqual(rrtype, kDNSType_CNAME);
260 XCTAssertEqual(rrclass, kDNSClass_IN);
261 ConvertDomainNameToCString((const domainname *const)rdata, domainname_cstr);
262 XCTAssertFalse(strcmp(domainname_cstr, "test212.dotbennu.com."));
264 // The mDNS_Execute call generates an add event for the A record
265 m->NextScheduledEvent = mDNS_TimeNow_NoLock(m);
268 // Verify the client's reply contains a properly formed add event for the A record.
269 reply = req->replies;
270 XCTAssertNotEqual(reply, (reply_state*)mDNSNULL);
271 XCTAssertNotEqual(reply->next, (reply_state*)mDNSNULL);
274 data = (char *)&reply->rhdr[1];
275 end = data+reply->totallen;
276 get_string(&data, data+reply->totallen, name, kDNSServiceMaxDomainName);
277 rrtype = get_uint16(&data, end);
278 rrclass = get_uint16(&data, end);
279 rdlen = get_uint16(&data, end);
280 rdata = get_rdata(&data, end, rdlen);
281 len = get_reply_len(name, rdlen);
283 XCTAssertEqual(reply->totallen, len + sizeof(ipc_msg_hdr));
284 XCTAssertEqual(reply->mhdr->version, VERSION);
285 XCTAssertEqual(reply->mhdr->datalen, len);
287 XCTAssertEqual(reply->mhdr->ipc_flags, 0);
288 XCTAssertEqual(reply->mhdr->op, query_reply_op);
290 XCTAssertEqual(reply->rhdr->flags, htonl(kDNSServiceFlagsAdd));
291 XCTAssertEqual(reply->rhdr->ifi, kDNSServiceInterfaceIndexAny);
292 XCTAssertEqual(reply->rhdr->error, kDNSServiceErr_NoError);
294 XCTAssertEqual(rrtype, kDNSType_A);
295 XCTAssertEqual(rrclass, kDNSClass_IN);
296 XCTAssertEqual(rdata[0], dns_response_ipv4.b[0]);
297 XCTAssertEqual(rdata[1], dns_response_ipv4.b[1]);
298 XCTAssertEqual(rdata[2], dns_response_ipv4.b[2]);
299 XCTAssertEqual(rdata[3], dns_response_ipv4.b[3]);
302 // This function verifies the cache and event handling occurred as expected when a network change happened.
303 // The uDNS_SetupDNSConfig is called to simulate a network change and two outcomes occur. First the A record
304 // query is restarted and sent to a new DNS server. Second the cache records are purged. Then mDNS_Execute
305 // is called and it removes the purged cache records and generates a remove event for the A record.
306 // The following are verified:
307 // 1.) The restart of query for A record.
308 // 2.) The cache is empty after mDNS_Execute removes the cache entres.
309 // 3.) The remove event is verified by examining the request's reply data.
310 - (void)_simulateNetworkChangeAndVerify
312 mDNS *const m = &mDNSStorage;
313 request_state* req = client_request_message;
314 DNSQuestion* q = &req->u.queryrecord.op.q;
315 mDNSu32 CacheUsed =0, notUsed =0;
316 const char *data; const char *end;
317 char name[kDNSServiceMaxDomainName];
318 uint16_t rrtype, rrclass, rdlen;
322 // The uDNS_SetupDNSConfig reconfigures the resolvers so the A record query is restarted and
323 // both the CNAME and A record are purged.
324 force_uDNS_SetupDNSConfig_ut(m);
326 // Verify the A record query was restarted. This is done indirectly by noticing the transaction id and interval have changed.
327 XCTAssertEqual(q->ThisQInterval, InitialQuestionInterval);
328 XCTAssertNotEqual(q->TargetQID.NotAnInteger, uDNS_TargetQID);
330 // Then mDNS_Execute removes both records from the cache and calls the client back with a remove event for A record.
331 m->NextScheduledEvent = mDNS_TimeNow_NoLock(m);
334 // Verify the cache entries are removed
335 LogCacheRecords_ut(mDNS_TimeNow(m), &CacheUsed, ¬Used);
336 XCTAssertEqual(CacheUsed, m->rrcache_totalused);
337 XCTAssertEqual(CacheUsed, 0);
339 // Verify the A record's remove event is setup as expected in the reply data
340 struct reply_state *reply;
341 reply = req->replies;
342 XCTAssertNotEqual(reply, (reply_state*)mDNSNULL);
343 XCTAssertNotEqual(reply->next, (reply_state*)mDNSNULL);
344 XCTAssertNotEqual(reply->next->next, (reply_state*)mDNSNULL);
346 reply = reply->next->next; // Get to last event to verify remove event
347 data = (char *)&reply->rhdr[1];
348 end = data+reply->totallen;
349 get_string(&data, data+reply->totallen, name, kDNSServiceMaxDomainName);
350 rrtype = get_uint16(&data, end);
351 rrclass = get_uint16(&data, end);
352 rdlen = get_uint16(&data, end);
353 rdata = get_rdata(&data, end, rdlen);
354 len = get_reply_len(name, rdlen);
356 XCTAssertEqual(reply->totallen, reply->mhdr->datalen + sizeof(ipc_msg_hdr));
357 XCTAssertEqual(reply->mhdr->version, VERSION);
358 XCTAssertEqual(reply->mhdr->datalen, len);
359 XCTAssertEqual(reply->mhdr->ipc_flags, 0);
360 XCTAssertEqual(reply->mhdr->op, query_reply_op);
362 XCTAssertNotEqual(reply->rhdr->flags, htonl(kDNSServiceFlagsAdd));
363 XCTAssertEqual(reply->rhdr->ifi, kDNSServiceInterfaceIndexAny);
364 XCTAssertEqual(reply->rhdr->error, kDNSServiceErr_NoError);
366 XCTAssertEqual(rrtype, kDNSType_A);
367 XCTAssertEqual(rrclass, kDNSClass_IN);
368 XCTAssertEqual(rdata[0], dns_response_ipv4.b[0]);
369 XCTAssertEqual(rdata[1], dns_response_ipv4.b[1]);
370 XCTAssertEqual(rdata[2], dns_response_ipv4.b[2]);
371 XCTAssertEqual(rdata[3], dns_response_ipv4.b[3]);